package com.custom.action.core;

import com.custom.action.condition.DefaultColumnParseHandler;
import com.custom.action.core.annotations.*;
import com.custom.comm.enums.TableNameStrategy;
import com.custom.comm.exceptions.CustomCheckException;
import com.custom.comm.utils.*;
import com.custom.jdbc.configuration.CustomConfigHelper;
import com.custom.jdbc.configuration.DbGlobalConfig;
import com.custom.jdbc.handler.TypeHandler;
import com.custom.jdbc.utils.DbConnGlobal;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 实体类的解析对象
 * @author   Xiao-Bai
 * @since  2021/12/2 14:10
 **/
public class TableParseModel<T> implements Cloneable {

    private static final Logger logger = LoggerFactory.getLogger(TableParseModel.class);

    /**
     * 实体表的class对象
     */
    @Getter
    private Class<T> entityClass;
    /**
     * 表名称
     */
    @Getter
    private String table;
    /**
     * 表别名
     */
    @Getter
    private String alias;
    /**
     * 表说明
     */
    @Getter
    private String desc;

    /**
     * 数据源
     */
    @Getter
    private int order;
    /**
     * 表与实体对应的字段
     */
    @Getter
    private final List<Field> fields;
    /**
     * 全局配置
     */
    private DbGlobalConfig globalConfig;
    /**
     * 驼峰转下划线
     */
    private boolean underlineToCamel;
    /**
     * 对于{@link DbKey}注解的解析
     */
    @Getter
    private DbKeyParserModel<T> keyParserModel = null;
    /**
     * 对于{@link DbField}注解的解析
     */
    private final List<DbFieldParserModel<T>> fieldParserModels = new ArrayList<>();
    /**
     * 对于{@link DbRelated}注解的解析
     */
    @Getter
    private final List<DbRelationParserModel<T>> relatedParserModels = new ArrayList<>();
    /**
     * 对于{@link DbJoinTables}注解的解析
     */
    @Getter
    private final List<DbJoinFieldParserModel<T>> joinDbMappers = new ArrayList<>();
    /**
     * 对于{@link DbJoinTables}注解的解析
     */
    @Getter
    private final List<String> joinTableParserModels = new ArrayList<>();
    /**
     * 对于java属性字段到表字段的映射关系
     */
    @Getter
    private final Map<String, String> fieldMapper = new HashMap<>();
    /**
     * 对于表字段到java属性字段的映射关系
     */
    @Getter
    private final Map<String, String> columnMapper = new HashMap<>();
    /**
     * 属性信息描述集合
     */
    @Getter
    private List<PropertyDescriptor> propertyList;

    /**
     * 字段转换类型处理器的缓存
     */
    @Getter
    private final Map<String, TypeHandler<?>> fieldTypeHandlerMap = new HashMap<>();


    /**
     * 创建表结构
     */
    public String createTableSql() {
        StringBuilder createTableSql = new StringBuilder();
        StringJoiner columnCreateSql = new StringJoiner(Constants.SEPARATOR_COMMA_1);
        if (Objects.nonNull(keyParserModel)) {
            columnCreateSql.add(keyParserModel.createTableSql() + "\n");
        }

        List<DbFieldParserModel<T>> dbFieldParseModels = getDbFieldParseModels();
        dbFieldParseModels.stream()
                    .map(dbFieldParserModel -> dbFieldParserModel.createTableSql() + "\n")
                .forEach(columnCreateSql::add);

        if (keyParserModel != null) {
            columnCreateSql.add(" PRIMARY KEY("+ keyParserModel.getDbKey() +") ");
        }

        createTableSql.append(String.format("CREATE TABLE `%s` (\n%s)", this.table, columnCreateSql));

        if (JudgeUtil.isNotEmpty(this.desc)) {
            createTableSql.append(String.format(" COMMENT = '%s'", this.desc));
        }
        return createTableSql.toString();
    }

    /**
     * 删除表结构
     */
    public String dropTableSql() {
        return String.format("DROP TABLE IF EXISTS `%s`", this.table);
    }


    /**
     * 构建字段映射
     */
    private void buildPropertyMapper() {
        if (Objects.nonNull(keyParserModel)) {
            columnMapper.put(keyParserModel.getFieldSql(), keyParserModel.getKey());
            fieldMapper.put(keyParserModel.getKey(), keyParserModel.getFieldSql());
            fieldTypeHandlerMap.put(keyParserModel.getKey(), keyParserModel.getTypeHandler());
        }

        // 基础字段
        fieldParserModels.forEach(x -> {
            if (x.isDbField()) {
                columnMapper.put(x.getFieldSql(), x.getFieldName());
                fieldMapper.put(x.getFieldName(), x.getFieldSql());
            }
            //这里需要所有的java字段和表字段的映射
            fieldTypeHandlerMap.put(x.getFieldName(), x.getTypeHandler());
        });
        // 关联字段1
        joinDbMappers.forEach(x -> {
            columnMapper.put(x.getJoinName(), x.getFieldName());
            fieldMapper.put(x.getFieldName(), x.getJoinName());
            fieldTypeHandlerMap.put(x.getFieldName(), x.getTypeHandler());
        });

        // 关联字段2
        relatedParserModels.forEach(x -> {
            columnMapper.put(x.getFieldSql(), x.getFieldName());
            fieldMapper.put(x.getFieldName(), x.getFieldSql());
            fieldTypeHandlerMap.put(x.getFieldName(), x.getTypeHandler());
        });
    }

    /**
     * 默认构造方法为查询
     */
    TableParseModel(Class<T> cls) {
        // 初始化本对象属性
        this.initLocalProperty(cls);
        // 加载所有字段
        this.fields =  ReflectUtil.loadFields(this.entityClass);
        // 构建字段解析模板
        this.buildFieldModels();
        // 构建字段映射缓存
        this.buildPropertyMapper();
        // 创建字段描述信息
        this.createProperty();
        // 创建Lambda解析缓存
        DefaultColumnParseHandler.createColumnCache(this);
    }

    /**
     * 初始化本对象属性
     */
    private void initLocalProperty(Class<T> cls) {
        this.entityClass = cls;
        DbTable dbTable = cls.getAnnotation(DbTable.class);
        if (Objects.isNull(dbTable)) {
            throw new CustomCheckException(cls.getName() + " 未标注@DbTable注解");
        }

        this.table = dbTable.value();
        this.alias = dbTable.alias();
        this.desc = dbTable.desc();
        this.order = dbTable.order();
        CustomConfigHelper configHelper = DbConnGlobal.getConfigHelper(this.order);
        AssertUtil.notNull(configHelper, "找不到与" +  cls + "相匹配的数据源");

        this.globalConfig = configHelper.getDbGlobalConfig();
        this.underlineToCamel = this.globalConfig.getStrategy().isUnderlineToCamel();

        // 未填写表名的情况下，由策略生成表名
        if (StrUtils.isBlank(this.table)) {
            this.table = this.generateTableName(entityClass.getSimpleName());
        }
        this.propertyList = new ArrayList<>();
    }

    /**
     * 根据类名以及前缀生成表名
     */
    private String generateTableName(String name) {
        TableNameStrategy nameStrategy = globalConfig.getTableNameStrategy();
        if (nameStrategy == TableNameStrategy.APPEND) {
            return globalConfig.getTableNamePrefix() + name;
        }
        if (nameStrategy == TableNameStrategy.LOWERCASE) {
            return globalConfig.getTableNamePrefix() + name.toLowerCase(Locale.ROOT);
        }
        String prefix = globalConfig.getTableNamePrefix();
        if (StrUtils.isNotBlank(prefix)) {
            prefix = globalConfig.getTableNamePrefix() + Constants.UNDERLINE;
        }
        return prefix + StrUtils.camelToUnderline(name);
    }

    /**
     * 构造查询模板
     */
    private void buildFieldModels() {
        // 解析@DbJoinTables注解
        this.buildDbJoinTables(this.entityClass);

        for (Field field : fields) {
            // 只支持一个主键,多的主键会被覆盖
            if (field.isAnnotationPresent(DbKey.class)
                    && Objects.isNull(keyParserModel)) {
                keyParserModel = new DbKeyParserModel<>(field, this.table, this.alias, this.underlineToCamel);

            } else if (field.isAnnotationPresent(DbField.class)) {
                this.parseDbField(field);

            } else if (field.isAnnotationPresent(DbJoinField.class)) {
                this.parseDbJoinField(field);

            } else if (field.isAnnotationPresent(DbRelated.class)) {
                this.parseDbRelated(field);

            } else if (field.isAnnotationPresent(DbNotField.class)) {
                DbFieldParserModel<T> fieldParserModel = new DbFieldParserModel<>(field);
                fieldParserModels.add(fieldParserModel);

            } else if (field.isAnnotationPresent(DbLogic.class)) {
                DbLogicParserModel<T> logicParserModel = new DbLogicParserModel<>(field, this.table, this.alias,
                        this.underlineToCamel, false);
                fieldParserModels.add(logicParserModel);
            }

            else {
                DbFieldParserModel<T> fieldParserModel = new DbFieldParserModel<>(field, this.table, this.alias,
                        this.underlineToCamel, false);
                fieldParserModels.add(fieldParserModel);
            }

        }
    }

    private void parseDbRelated(Field field) {
        // 若当前是本类时，则注解生效，否则不视为表字段
        if (field.getDeclaringClass().equals(this.entityClass)) {
            DbRelationParserModel<T> relatedParserModel = new DbRelationParserModel<>(this.entityClass, field,
                    this.table, this.alias, this.underlineToCamel);
            relatedParserModels.add(relatedParserModel);
        }else {
            DbFieldParserModel<T> fieldParserModel = new DbFieldParserModel<>(field);
            fieldParserModels.add(fieldParserModel);
        }
    }

    private void parseDbJoinField(Field field) {
        DbJoinFieldParserModel<T> joinTableParserModel = new DbJoinFieldParserModel<>(this.entityClass, field);
        joinDbMappers.add(joinTableParserModel);
    }

    private void parseDbField(Field field) {
        DbField dbField = field.getAnnotation(DbField.class);
        DbLogic dbLogic = field.getAnnotation(DbLogic.class);
        DbFieldParserModel<T> fieldParserModel;

        if (dbField.exist()) {
            if (dbLogic != null) {
                fieldParserModel = new DbLogicParserModel<>(field, this.table, this.alias,
                        this.underlineToCamel, true);
            }else {
                fieldParserModel  = new DbFieldParserModel<>(field, this.table, this.alias,
                        this.underlineToCamel, true);
            }
        }else {
            fieldParserModel = new DbFieldParserModel<>(field);
        }
        fieldParserModels.add(fieldParserModel);
    }

    /**
     * 解析@DbJoinTable(s)
     */
    private void buildDbJoinTables(Class<?> target) {
        Class<?> targetClass = target;
        Stack<List<String>> joinListStack = new Stack<>();
        while (!targetClass.equals(Object.class)) {
            List<String> joinList = new ArrayList<>();
            DbJoinTables joinTables = targetClass.getAnnotation(DbJoinTables.class);
            if (Objects.nonNull(joinTables)) {
                Arrays.stream(joinTables.value()).map(DbJoinTable::value).forEach(joinList::add);
            }
            DbJoinTable joinTable = targetClass.getAnnotation(DbJoinTable.class);
            if (Objects.nonNull(joinTable)) {
                joinList.add(joinTable.value());
            }
            targetClass = targetClass.getSuperclass();
            joinListStack.push(joinList);
        }
        while (!joinListStack.isEmpty()) {
            this.joinTableParserModels.addAll(joinListStack.pop());
        }
    }

    /**
     * 查找逻辑删除字段
     */
    public Optional<DbLogicParserModel<T>> getLogicField() {
        return fieldParserModels.stream().filter(DbFieldParserModel::isLogical)
                .map(e -> (DbLogicParserModel<T>) e)
                .findFirst();
    }

    public List<DbFieldParserModel<T>> getDbFieldParseModels() {
        return fieldParserModels.stream().filter(DbFieldParserModel::isDbField)
                .collect(Collectors.toList());
    }


    /**
     * 初始化所有字段描述信息
     */
    private void createProperty() {
        try {
            this.propertyList = ReflectUtil.getProperties(this.entityClass);
        } catch (IntrospectionException e) {
            logger.error(e.toString(), e);
        }
    }


    @Override
    @SuppressWarnings("unchecked")
    public TableParseModel<T> clone() {
        try {
            return (TableParseModel<T>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
